package social.p2p.controller;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.swing.JOptionPane;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.document.Document;
import net.jxta.document.MimeMediaType;
import net.jxta.endpoint.*;
import net.jxta.id.ID;
import net.jxta.id.IDFactory;
import net.jxta.impl.id.UUID.PipeID;
import net.jxta.impl.protocol.PipeAdv;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.pipe.PipeService;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.socket.JxtaSocket;
import net.jxta.util.SimpleSelector;
import org.apache.commons.io.IOUtils;
import social.p2p.exception.SocialException;
import social.p2p.model.*;
import social.p2p.ui.ProcessForm;
import social.p2p.util.BufferedImageBuilder;

/**
 *
 * @author cadmilo
 */
public class Socialp2pController extends Observable {

    private final static EntityManagerFactory emf;
    private final static int MIN_PHOTOS = 2;
    private EntityManager em;
    private final static String RUTA_CACHE = "data/cache";
    private NetworkManager manager;
    private PeerGroup netPeerGroup;
    private DiscoveryService discovery;
    private PipeAdvertisement pipeAdv;
    private String nombreNodo = UUID.randomUUID().toString();
    private String nombreNodoPropio;
    private AdvertisementManager advertisementManager;
    private TransferThread tt;
    private boolean test;
    private net.sf.atomicdate.Date fechaInicio = new net.sf.atomicdate.Date();
    // Timer to update nodes
    Timer timer = new Timer();
    // 
    private ArrayList<NodoFotoAdvertisement> nodes = new ArrayList<NodoFotoAdvertisement>();
    //Administrador de la SC
    private AdministradorAdvertisement admin;
    
    static {
        emf = Persistence.createEntityManagerFactory("social-p2pPU");
    }

    public Socialp2pController(boolean test) {
        this.test = test;
        JDBCConnectionProvider.test = test;
    }

    public void init() {
        try {
            em = emf.createEntityManager();
            em.getTransaction().begin();
            // Crear la tabla por defecto
            try {
                em.getTransaction().begin();
                em.createNativeQuery(
                        "CREATE TABLE foto( "
                        + "id varchar(150), "
                        + "user_id varchar(150) primary key, "
                        + "foto BLOB(15000000), "
                        + "foto_1 BLOB(15000000), "
                        + "foto_2 BLOB(15000000), "
                        + "tiempo_registro timestamp, "
                        + "utc_registro int, "
                        + "tiempo_descarga timestamp, "
                        + "utc_descarga int, "
                        + "ancho int, "
                        + "alto int, "
                        + "tamanio double,  "
                        + "personas varchar(10000), "
                        + "tags varchar(2000), "
                        + "descargada boolean, "
                        + "tipo varchar(1000), "
                        + "afinidad varchar(1000) )").executeUpdate();
                em.getTransaction().commit();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            // Crear la tabla por defecto para estados locales del nodo
            try {
                em.getTransaction().begin();
                em.createNativeQuery(
                        "CREATE TABLE estado( "
                        + "id varchar(150), "
                        + "metodo varchar(150), "
                        + "parametros varchar(300) )").executeUpdate();
                em.getTransaction().commit();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            // Crear la tabla por defecto para estado global
            try {
                em.getTransaction().begin();
                em.createNativeQuery(
                        "CREATE TABLE estadoglobal( "
                        + "id varchar(150), "
                        + "nombrenodo varchar(150), "
                        + "metodo varchar(150), "
                        + "parametros varchar(300) )").executeUpdate();
                em.getTransaction().commit();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            manager = new NetworkManager(NetworkManager.ConfigMode.ADHOC, nombreNodo, new File(RUTA_CACHE, nombreNodo).toURI());
            manager.startNetwork();
            manager.getNetPeerGroup().getMembershipService().addPropertyChangeListener(new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent pce) {
                    throw new UnsupportedOperationException("Not supported yet.");
                }
            });
            AdvertisementFactory.registerAdvertisementInstance(FotoAdvertisement.class.getName(), AdvertisementManager.getInitiator(FotoAdvertisement.class));
            AdvertisementFactory.registerAdvertisementInstance(NodoFotoAdvertisement.class.getName(), AdvertisementManager.getInitiator(NodoFotoAdvertisement.class));
            netPeerGroup = manager.getNetPeerGroup();
            PeerGroupID id = netPeerGroup.getPeerGroupID();
            PipeID idPipe = (PipeID) IDFactory.newPipeID(id);
            pipeAdv = crearPipeAdvertisement(idPipe);
            discovery = netPeerGroup.getDiscoveryService();
            advertisementManager = new AdvertisementManager(discovery, pipeAdv, this);
            discovery.publish(pipeAdv);
            tt = new TransferThread(this,netPeerGroup, pipeAdv);
            nombreNodoPropio = JOptionPane.showInputDialog("Ingrese el nombre de su nodo");
            if (nombreNodoPropio == null || nombreNodoPropio.isEmpty()) {
                JOptionPane.showMessageDialog(null, "El nombre no puede estar vacio.");
                manager.stopNetwork();
                System.exit(0);
            }
//            manager.getNetPeerGroup().getEndpointService().addMessengerEventListener(new MessengerEventListener() {
//
//                @Override
//                public boolean messengerReady(MessengerEvent me) {
//                    System.out.print("!!!!Mensaje de que algo paso en la red");
//                    return false;
//                }
//            }, 12);
            advertisementManager.publisNodoAdvertisement(nombreNodoPropio, fechaInicio.getTime() + "");
            tt.start();
            publishStoredPhotos();
            timer.schedule(new TimerTask() {

                @Override
                public void run() {
                    searchForOtherNodes();
                }
            }, new Date(), 5000);

//            FutureTask f = new FutureTask(new Callable() {
//
//                @Override
//                public Object call() throws Exception {
//                    throw new UnsupportedOperationException("Not supported yet.");
//                }
//            });
//            f.get(MIN_PHOTOS, TimeUnit.DAYS);
           //Iniciar busqueda de nodo coordinador
           // iniciarProcesoEleccion();
        } catch (Exception ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private PipeAdvertisement crearPipeAdvertisement(ID pipeId) {
        PipeAdvertisement advertisement = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(PipeAdvertisement.getAdvertisementType());
        advertisement.setPipeID(pipeId);
        advertisement.setType(PipeService.UnicastType);
        advertisement.setName("Pipe social p2p");
        return advertisement;
    }

    public static EntityManager getEM() {
        return emf.createEntityManager();
    }

    public static int compare(byte[] localPhoto, byte[] remotePhoto) {
        return new Double(Math.random()).intValue();
    }

    public void addPhoto(String id, String personas, File sPhoto) throws FileNotFoundException {
        addPhoto(id, personas, getType(sPhoto.getName()), new FileInputStream(sPhoto), null, null, false);
    }

    public void addPhoto(String id, String personas, String type, InputStream sPhoto, Date tiempoRegistro, Integer utcRegistro, boolean descargada) {
        try {
            Foto f = new Foto();
            byte[] foto = IOUtils.toByteArray(sPhoto);
            BufferedImage img = ImageIO.read(new ByteArrayInputStream(foto));
            ProcessForm.setText("Calculando Afinidad");
            f.setAfinidad(getAffinity(img));
            f.setAlto(img.getHeight());
            f.setAncho(img.getWidth());
            f.setDescargada(descargada);
            f.setFoto(foto);
            f.setTipo(type);
            ProcessForm.setText("Generando imagenes");
            f.setFoto1(getSmallImage(img, type, 1));
            f.setFoto2(getSmallImage(img, type, 2));
            f.setId(UUID.randomUUID().toString());
            f.setPersonas(personas);
            f.setTamanio(new Double(f.getFoto().length));
            if (!descargada) {
                f.setTiempoDescarga(null);
            } else {
                f.setTiempoDescarga(new Date());
            }
            if (tiempoRegistro == null) {
                tiempoRegistro = new Date();
            }
            f.setTiempoRegistro(tiempoRegistro);
            f.setUserId(id);
            if (!descargada) {
                f.setUtcDescarga(null);
            } else {
                f.setUtcDescarga(TimeZone.getDefault().getRawOffset());
            }
            if (utcRegistro == null) {
                utcRegistro = TimeZone.getDefault().getRawOffset();
            }
            f.setUtcRegistro(utcRegistro);
            em.getTransaction().begin();
            em.persist(f);
            em.getTransaction().commit();
            ProcessForm.setText("Publicando Advertisement");
            advertisementManager.publishPhotoAdvertisement(f);
        } catch (IOException ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            throw new SocialException("Error cargando publicando la foto");
        }
    }
    
    public void addEstado(String metodo, String parametros) {
        try {
            Estado e = new Estado();
            e.setMetodo(metodo);
            e.setParametros(parametros);
            em.getTransaction().begin();
            em.persist(e);
            em.getTransaction().commit();
            
            //Prueba lista de estados
            /*em.getTransaction().begin();
            List<Estado> resp = em.createNamedQuery("Estado.selectAll").getResultList();
            em.getTransaction().commit();
            System.out.println("--LISTA DE ESTADOS EN LA BD--");
            for(Estado est : resp) {
                System.out.println(est.getId()+est.getMetodo()+est.getParametros());
            } */
        } catch (Exception ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            throw new SocialException("Error añadiendo el estado local");
        }
    }
    
    public void deleteEstado(String metodo, String parametros) {
        try {
            Estado e = new Estado();
            e.setMetodo(metodo);
            e.setParametros(parametros);
            em.getTransaction().begin();
            em.remove(e);
            em.getTransaction().commit();
            
            //Prueba lista de estados
            /*em.getTransaction().begin();
            List<Estado> resp = em.createNamedQuery("Estado.selectAll").getResultList();
            em.getTransaction().commit();
            System.out.println("--LISTA DE ESTADOS EN LA BD--");
            for(Estado est : resp) {
                System.out.println(est.getId()+est.getMetodo()+est.getParametros());
            } */
        } catch (Exception ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            throw new SocialException("Error borrando el estado local");
        }
    }
    
    public void addEstadoGlobal(String nombreNodo, String metodo, String parametros) {
        try {
            EstadoGlobal e = new EstadoGlobal();
            e.setNombreNodo(nombreNodo);
            e.setMetodo(metodo);
            e.setParametros(parametros);
            em.getTransaction().begin();
            em.persist(e);
            em.getTransaction().commit();
            
            //Prueba lista de estados globales
            em.getTransaction().begin();
            List<EstadoGlobal> resp = em.createNamedQuery("EstadoGlobal.selectAll").getResultList();
            em.getTransaction().commit();
            System.out.println("--LISTA DE ESTADOS GLOBALES EN LA BD--");
            for(EstadoGlobal est : resp) {
                System.out.println(est.getNombreNodo()+est.getId()+est.getMetodo()+est.getParametros());
            }
        } catch (Exception ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            throw new SocialException("Error añadiendo el estado global");
        }
    }
    
    public void deleteEstadoGlobal(String nombreNodo, String metodo, String parametros) {
        try {
            EstadoGlobal e = new EstadoGlobal();
            e.setNombreNodo(nombreNodo);
            e.setMetodo(metodo);
            e.setParametros(parametros);
            em.getTransaction().begin();
            em.remove(e);
            em.getTransaction().commit();
            
            //Prueba lista de estados globales
            em.getTransaction().begin();
            List<EstadoGlobal> resp = em.createNamedQuery("EstadoGlobal.selectAll").getResultList();
            em.getTransaction().commit();
            System.out.println("--LISTA DE ESTADOS GLOBALES EN LA BD--");
            for(EstadoGlobal est : resp) {
                System.out.println(est.getNombreNodo()+est.getId()+est.getMetodo()+est.getParametros());
            }
        } catch (Exception ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            throw new SocialException("Error borrando el estado global");
        }
    }

    public static String getAffinity(BufferedImage img) {
        BufferedImage bAffinity = BufferedImageBuilder.toBufferedImage(img.getScaledInstance(1, 1, Image.SCALE_FAST));
        int red = bAffinity.getColorModel().getRed(1);
        int green = bAffinity.getColorModel().getGreen(1);
        int blue = bAffinity.getColorModel().getBlue(1);
        if (red > green && red > blue) {
            return "red";
        }
        if (green > red && green > blue) {
            return "green";
        }
        if (blue > green && blue > red) {
            return "blue";
        }
        return null;
    }

    public static byte[] getSmallImage(BufferedImage originalImage, String type, int reduction) {
        try {
            Image rImage = originalImage.getScaledInstance(originalImage.getWidth() / (reduction + 1), -100, Image.SCALE_FAST);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            String informalName = null;
            ImageIO.write(BufferedImageBuilder.toBufferedImage(rImage), type, bout);
            bout.close();
            return bout.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static String getType(String name) {
        if (name.toLowerCase().endsWith(".jpg")) {
            return "JPG";
        } else if (name.toLowerCase().endsWith(".jpeg")) {
            return "JPEG";
        } else if (name.toLowerCase().endsWith(".gif")) {
            return "GIF";
        } else if (name.toLowerCase().endsWith(".png")) {
            return "PNG";
        }
        return null;
    }

    public void searchPhotosSinEstado(String field, String searchText) {
        advertisementManager.searchPhotoAdvertisement(field, searchText);
    }
    
    public void searchPhotosConEstado(String field, String searchText) {
        //Tocó separar con | porque el ; lo usamos en el protocolo de los sockets
        System.out.println("Guardando el estado " + "searchPhotosSinEstado " + field.concat("|").concat(searchText));
        addEstado("searchPhotosSinEstado", field.concat(";").concat(searchText));
        searchPhotosSinEstado(field, searchText);
        System.out.println("Borrando el estado " + "searchPhotosSinEstado " + field.concat("|").concat(searchText));
        deleteEstado("searchPhotosSinEstado", field.concat(";").concat(searchText));
    }

    public void searchPhotoResult(ArrayList<FotoAdvertisement> advs, int searchId) {
        setChanged();
        SearchEvent se = new SearchEvent(advs, searchId);
        notifyObservers(se);
    }

    public void downloadFotoSinEstado(FotoAdvertisement fotoAdvertisement) {
        OutputStream out = null;
        InputStream in = null;
        PrintWriter outWriter = null;
        JxtaSocket socket = null;
        try {
            socket = new JxtaSocket(netPeerGroup, fotoAdvertisement.getPipeAdv());
            in = socket.getInputStream();
            out = socket.getOutputStream();
            outWriter = new PrintWriter(out, true);
            outWriter.println(TransferThread.GET_OPERATION + TransferThread.SEPARATOR + fotoAdvertisement.getId());
            ByteArrayOutputStream bout = new ByteArrayOutputStream(new Integer(fotoAdvertisement.getTamanio()));
            IOUtils.copy(in, bout);
            bout.flush();
            outWriter.println("OK");
            Foto f = AdvertisementManager.getFoto(fotoAdvertisement);
            addPhoto(fotoAdvertisement.getUserId(), fotoAdvertisement.getPersonas(), fotoAdvertisement.getTipo(), new ByteArrayInputStream(bout.toByteArray()), f.getTiempoRegistro(), f.getUtcRegistro(), true);
        } catch (IOException ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                out.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                socket.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }
    
    public void downloadFotoConEstado(FotoAdvertisement fotoAdvertisement) {
        System.out.println("Guardando el estado " + "downloadFotoSinEstado " + fotoAdvertisement.getId());
        addEstado("downloadFotoSinEstado", fotoAdvertisement.getId());
        downloadFotoSinEstado(fotoAdvertisement);
        System.out.println("Borrando el estado " + "downloadFotoSinEstado " + fotoAdvertisement.getId());
        deleteEstado("downloadFotoSinEstado", fotoAdvertisement.getId()); 
    }
    
    public void guardarEstadoGlobal() {
        OutputStream out = null;
        InputStream in = null;
        PrintWriter outWriter = null;
        JxtaSocket socket = null;
        BufferedReader inReader = null;
        //Guardamos el estado de cada nodo, el local ya se hace interno
        //No debemos comunicarnos con nosotros mismos... sólo con el resto, chequeamos por nombre del nodo
        for(NodoFotoAdvertisement nodo : nodes) {
            //System.out.println("Nombre del nodo en la lista: "+nodo.getName());
            //System.out.println("Nombre del nodo propio: "+nombreNodoPropio);
            if(!nodo.getName().equals(nombreNodoPropio)) {
                try {
                    System.out.println("Comunicándose con el nodo "+nodo.getName()+ " para traer el estado");
                    socket = new JxtaSocket(netPeerGroup, nodo.getPipeAdv());
                    in = socket.getInputStream();
                    out = socket.getOutputStream();
                    outWriter = new PrintWriter(out, true);
                    inReader = new BufferedReader(new InputStreamReader(in));
                    outWriter.println(TransferThread.GET_STATE_OPERATION + TransferThread.SEPARATOR);
                    outWriter.println("OK");
                    String[] response = inReader.readLine().split(";");
                    while(!response[0].startsWith("OK")) {
                        if (response[0].startsWith(TransferThread.ESTADO)) {
                            //El método viene en el segundo campo, los parámetros vienen en el tercer campo
                            addEstadoGlobal(nodo.getName(), response[1], response[2]);
                        }
                        //Leer la siguiente línea... hasta encontrar el OK
                        response = inReader.readLine().split(";");
                    }
                } catch (IOException ex) {
                    Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    try {
                        in.close();
                    } catch (IOException ex) {
                        Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    try {
                        out.close();
                    } catch (IOException ex) {
                        Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    try {
                        socket.close();
                    } catch (IOException ex) {
                        Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
        System.out.println("Estado global guardado");
    }
    
    public void recuperarEstadoGlobal() {
        OutputStream out = null;
        InputStream in = null;
        PrintWriter outWriter = null;
        JxtaSocket socket = null;
        BufferedReader inReader = null;
        //Recuperamos el estado de cada nodo, el local ya se hace interno
        //No debemos comunicarnos con nosotros mismos... sólo con el resto, chequeamos por nombre del nodo
        for(NodoFotoAdvertisement nodo : nodes) {
            //System.out.println("Nombre del nodo en la lista: "+nodo.getName());
            //System.out.println("Nombre del nodo propio: "+nombreNodoPropio);
            if(!nodo.getName().equals(nombreNodoPropio)) {
                try {
                    System.out.println("Comunicándose con el nodo "+nodo.getName()+ " para enviarle el estado");
                    socket = new JxtaSocket(netPeerGroup, nodo.getPipeAdv());
                    in = socket.getInputStream();
                    out = socket.getOutputStream();
                    outWriter = new PrintWriter(out, true);
                    inReader = new BufferedReader(new InputStreamReader(in));
                    outWriter.println(TransferThread.SET_STATE_OPERATION + TransferThread.SEPARATOR);
                    System.out.println("Enviando el estado al nodo: ");
                    em.getTransaction().begin();
                    List<EstadoGlobal> e = em.createNamedQuery("EstadoGlobal.getEstadoGlobalporNombreNodo").setParameter(1, nodo.getName()).getResultList();
                    em.getTransaction().commit();
                    //Si no hay operaciones pendientes de ese nodo no se envía nada (estado idle o vacío)
                    for(EstadoGlobal est: e) {
                        outWriter.println(TransferThread.ESTADO + TransferThread.SEPARATOR + est.getMetodo() + TransferThread.SEPARATOR + est.getParametros());
                        deleteEstadoGlobal(nodo.getName(), est.getMetodo(), est.getParametros());
                    }
                    outWriter.println("OK");
                    out.flush();
                    em.close();
                } catch (IOException ex) {
                    Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    try {
                        in.close();
                    } catch (IOException ex) {
                        Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    try {
                        out.close();
                    } catch (IOException ex) {
                        Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    try {
                        socket.close();
                    } catch (IOException ex) {
                        Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
        System.out.println("Estado global enviado a los nodos para recuperación del estado");
        //Recuperación del estado local
        em.getTransaction().begin();
        List<Estado> listaEstados = em.createNamedQuery("Estado.selectAll").getResultList();
        em.getTransaction().commit();
        for(Estado e : listaEstados) {
            ejecutarAccion(e);
            deleteEstado(e.getMetodo(), e.getParametros());
        }
        System.out.println("Estado local recuperado");
    }

    public void ejecutarAcciones(List<Estado> listaEstados) {
        for(Estado e : listaEstados) {
            String[] parametros = e.getParametros().split("|");
            if(e.getMetodo().equals("searchPhotosSinEstado")) { 
                searchPhotosSinEstado(parametros[0], parametros[1]);
            } else if(e.getMetodo().equals("searchLikeSinEstado")) { 
                searchLikeSinEstado(new File(parametros[0]));
            } else if(e.getMetodo().equals("downloadFotoSinEstado")) { 
                //Toca buscar el fotoAdv y luego si mandar bajar?
                //searchPhotosSinEstado("userId", parametros[0]);
            }
            deleteEstado(e.getMetodo(), e.getParametros());
        }
    }
    
    public void ejecutarAccion(Estado e) {
        String[] parametros = e.getParametros().split("|");
        if(e.getMetodo().equals("searchPhotosSinEstado")) { 
            searchPhotosSinEstado(parametros[0], parametros[1]);
        } else if(e.getMetodo().equals("searchLikeSinEstado")) { 
            searchLikeSinEstado(new File(parametros[0]));
        } else if(e.getMetodo().equals("downloadFotoSinEstado")) { 
            //Toca buscar el fotoAdv y luego si mandar bajar?
            //searchPhotosSinEstado("userId", parametros[0]);
        }
    }
    
    public List<Foto> getPhotos() {
        em.getTransaction().begin();
        List<Foto> resp = em.createNamedQuery("Foto.selectAll").getResultList();
        em.getTransaction().commit();
        return resp;
    }

    public List<Foto> getLocalPhotos() {
        em.getTransaction().begin();
        List<Foto> resp = em.createNamedQuery("Foto.selectAllLocal").getResultList();
        em.getTransaction().commit();
        return resp;
    }

    public List<Foto> getDownloadedPhotos() {
        em.getTransaction().begin();
        List<Foto> resp = em.createNamedQuery("Foto.selectAllRemote").getResultList();
        em.getTransaction().commit();
        return resp;
    }

    private void publishStoredPhotos() {
        List<Foto> fotos = getPhotos();
        for (Foto foto : fotos) {
            try {
                advertisementManager.publishPhotoAdvertisement(foto);
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        setChanged();
        notifyObservers(new UpdateListEvent("Todas", fotos));
    }
    private File selectedFile;

    public void searchLikeSinEstado(File selectedImage) {
        try {
            comparison = false;
            foundedPhotos = new ArrayList<FotoAdvertisement>();
            selectedFile = selectedImage;
            BufferedImage img = ImageIO.read(new FileInputStream(selectedImage));
            String af = getAffinity(img);
            advertisementManager.searchPhotoAffinityAdvertisement(af);
        } catch (IOException ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void searchLikeConEstado(File selectedImage) {
        System.out.println("Guardando el estado " + "searchLikeSinEstado " + selectedImage.getPath());
        addEstado("searchLikeSinEstado", selectedImage.getPath());
        searchLikeSinEstado(selectedImage);
        System.out.println("Borrando el estado " + "searchLikeSinEstado " + selectedImage.getPath());
        deleteEstado("searchLikeSinEstado", selectedImage.getPath());
    }
    
    private ArrayList<FotoAdvertisement> foundedPhotos;
    private boolean comparison;

    public synchronized void searchAffinityPhotoResult(ArrayList<FotoAdvertisement> resp, int queryID) {
        ProcessForm.setText("Procesando Fotografías encontradas con la misma afinidad");
        if (!comparison) {
            for (FotoAdvertisement fotoAdvertisement : resp) {
                try {
                    fotoAdvertisement.setAf(calculateAffinityValue(fotoAdvertisement, IOUtils.toByteArray(new FileInputStream(selectedFile))));
                    foundedPhotos.add(fotoAdvertisement);
                } catch (Exception ex) {
                    Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            if (foundedPhotos.size() >= MIN_PHOTOS && !comparison) {
                comparePhotos();
            }
        }
    }

    private void comparePhotos() {
        comparison = true;
        ProcessForm.setText("Escogiendo la mejor fotografía");
        Collections.sort(foundedPhotos, new Comparator<FotoAdvertisement>() {

            @Override
            public int compare(FotoAdvertisement t, FotoAdvertisement t1) {
                Integer a1 = t.getAf();
                Integer a2 = t1.getAf();
                return a1.compareTo(a2);
            }
        });
        ProcessForm.setText("Descargando la fotografía");
        downloadFotoSinEstado(foundedPhotos.get(0));
        setChanged();
        notifyObservers(new SearchLikeEvent(foundedPhotos.get(0)));
        foundedPhotos.clear();
        ProcessForm.hideIt();
    }

    private int calculateAffinityValue(FotoAdvertisement fotoAdvertisement, byte[] foto) {
        OutputStream out = null;
        InputStream in = null;
        PrintWriter outWriter = null;
        BufferedReader inReader = null;
        JxtaSocket socket = null;
        try {
            socket = new JxtaSocket(netPeerGroup, fotoAdvertisement.getPipeAdv());
            in = socket.getInputStream();
            out = socket.getOutputStream();
            inReader = new BufferedReader(new InputStreamReader(in));
            outWriter = new PrintWriter(out, false);
            outWriter.println(TransferThread.COMPARE_OPERATION + TransferThread.SEPARATOR + fotoAdvertisement.getId() + TransferThread.SEPARATOR + foto.length);
            outWriter.flush();
            ByteArrayInputStream is = new ByteArrayInputStream(foto);
            System.out.println("Enviado :" + foto.length);
            out.write(foto);
            out.flush();
            return new Integer(inReader.readLine());
            //return 1;
        } catch (IOException ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                out.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                socket.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return -1;
    }

    private void searchForOtherNodes() {
        advertisementManager.searchNodes();
    }

    public void searchForOtherNodesResult(ArrayList<NodoFotoAdvertisement> resp) {
        nodes.removeAll(resp);
        nodes.addAll(resp);
        for (NodoFotoAdvertisement nodoFotoAdvertisement : resp) {
            System.out.println("Se encontro un nodo" + nodoFotoAdvertisement.getName());
            OutputStream out = null;
            InputStream in = null;
            PrintWriter outWriter = null;
            BufferedReader inReader = null;
            JxtaSocket socket = null;
//        try {
//            socket = new JxtaSocket(netPeerGroup, fotoAdvertisement.getPipeAdv());
//            in = socket.getInputStream();
//            out = socket.getOutputStream();
//            inReader = new BufferedReader(new InputStreamReader(in));
//            outWriter = new PrintWriter(out, false);
//            outWriter.println(TransferThread.COMPARE_OPERATION + TransferThread.SEPARATOR + fotoAdvertisement.getId() + TransferThread.SEPARATOR + foto.length);
//            outWriter.flush();
//            ByteArrayInputStream is = new ByteArrayInputStream(foto);
//            System.out.println("Enviado :" + foto.length);
//            out.write(TransferThread.PING_OPERATION);
//            out.flush();
//            //return 1;
//        } catch (IOException ex) {
//            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
//        } finally {
//            try {
//                in.close();
//            } catch (IOException ex) {
//                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
//            }
//            try {
//                out.close();
//            } catch (IOException ex) {
//                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
//            }
//            try {
//                socket.close();
//            } catch (IOException ex) {
//                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }
        }
    }

    public void iniciarProcesoEleccion() {
        ProcessForm.show("Buscando el nodo coordinador");
        ArrayList<NodoFotoAdvertisement> nnodos = new ArrayList<NodoFotoAdvertisement>();
        Collections.copy(nnodos, nodes);
        for (NodoFotoAdvertisement nodoFotoAdvertisement : nnodos) {
            long ctime = new Long(nodoFotoAdvertisement.getConnectionTime());
            if(ctime<fechaInicio.getTime()){
                if(iniciarEleccion(nodoFotoAdvertisement)){
                    break;
                }
            }
        }
        anunciarseComoCoordinador();
    }
    
    public void anunciarseComoCoordinador(){
        
    }
    
    public boolean iniciarEleccion(NodoFotoAdvertisement fadv){
        OutputStream out = null;
            InputStream in = null;
            PrintWriter outWriter = null;
            BufferedReader inReader = null;
            JxtaSocket socket = null;
        try {
            socket = new JxtaSocket(netPeerGroup, fadv.getPipeAdv());
            in = socket.getInputStream();
            out = socket.getOutputStream();
            inReader = new BufferedReader(new InputStreamReader(in));
            outWriter = new PrintWriter(out, true);
            outWriter.println(TransferThread.INIT_ELECTION_OPERATION);
            return true;
        } catch (IOException ex) {
            Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                out.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                socket.close();
            } catch (IOException ex) {
                Logger.getLogger(Socialp2pController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
